/*
 * Copyright 2007 Brian Watkins
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

package bw.net;

import java.net.*;
import java.io.*;
import java.util.*;

import bw.util.*;

public class NetResource
{
    public static final int NETWORK_DOWN = 0;
    public static final int HTTP_NOT_OK = 1;

    private static Hashtable CONDITIONAL_GET_TABLE = new Hashtable();

    private URL _url = null;
    private String _username = null;
    private String _password = null;
    private int _error = -1;
    private int _httpResponseCode = -1;
    private boolean _doConditionalGET = false;
    private boolean _shouldGET = true;

    public NetResource ( String url )
        throws MalformedURLException
    {
        _url = new URL( url );
    }

    public NetResource ( String url, String username, String password )
        throws MalformedURLException
    {
        _url = new URL( url );
        _username = username;
        _password = password;
    }

    public void doConditionalGET ()
    {
        //we will attempt to do a conditional GET. header field info
        //is stored in the static CONDITIONAL_GET_TABLE. users of this
        //feature should call shouldGET() to see if they should attempt
        //to read the resource
        _doConditionalGET = true;
    }

    public boolean shouldGET ()
    {
        //should I read the resource after a conditional GET?
        return _shouldGET;
    }

    public int getError () { return _error; }

    public HttpURLConnection getURLConnection () throws IOException
    {
        HttpURLConnection conn = null;

        try
        {
            conn = (HttpURLConnection) _url.openConnection();

            conn.setUseCaches( false );
            conn.setRequestProperty( "User-Agent", "WD-2.0" );

            if ( _doConditionalGET )
            {
                //set the correct headers for handling conditional get if
                //we need to and that info has been stored from a previous request
                ResourceInfo ri = (ResourceInfo) CONDITIONAL_GET_TABLE.get( _url.toString() );
                if ( ri != null )
                {
                    if ( ri.lastModified != null )
                    {
                        conn.setRequestProperty( "If-Modified-Since", ri.lastModified );
                    }

                    if ( ri.etag != null )
                    {
                        conn.setRequestProperty( "If-None-Match", ri.etag );
                    }
                }
            }

            //do the authentication if we need to
            if ( _username != null && _password != null )
            {
                String authenticationStr = _username + ":" + _password;
                sun.misc.BASE64Encoder encoder = new sun.misc.BASE64Encoder();
                String encodedAuthenticationStr = encoder.encode( authenticationStr.getBytes() );
                conn.setRequestProperty( "Authorization", "Basic " + encodedAuthenticationStr );
            }

            //handle response codes. 
            _httpResponseCode = conn.getResponseCode();

            if ( _httpResponseCode == HttpURLConnection.HTTP_OK )
            {
                if ( _doConditionalGET ) 
                {
                    ResourceInfo ri = new ResourceInfo();
                    ri.lastModified = conn.getHeaderField( "Last-Modified" );
                    ri.etag = conn.getHeaderField( "ETag" );
                    
                    CONDITIONAL_GET_TABLE.put( _url.toString(), ri );
                }
            }
            else if ( _httpResponseCode == HttpURLConnection.HTTP_NOT_MODIFIED )
            {
                //go ahead and return the url connection even though there is 
                //nothing in the body. users of this class should check shouldGET()
                //before reading from the connection if they want to handle 
                //conditional GETs. Don't return null because that means an error
                //occurred.
                _shouldGET = false;
            }
            else
            {
                Log.getInstance().write( "Error getting url: " + _url + "\n" +
                                         "Error code: " + _httpResponseCode );
                _error = HTTP_NOT_OK;
                conn.disconnect();
                conn = null;
            }

        }
        catch ( SocketException ex )
        {
            //network is down
            conn.disconnect();
            conn = null;
            _error = NETWORK_DOWN;
        }
        
        return conn;
    }

    public InputStream getInputStream () throws IOException
    {
        InputStream in = null;

        HttpURLConnection conn = getURLConnection();
        if ( conn != null )
        {
            in = conn.getInputStream();
        }

        return in;
    }
    
    public int getResponseCode ()
    {
        return _httpResponseCode;
    }

    public String toString ()
    {
        return _url.toString();
    }

    class ResourceInfo
    {
        public String lastModified = null;
        public String etag = null;

        public ResourceInfo ()
        {
            //nothing
        }
    }
}